#include "head.hpp"
#include "debug.hpp"
#include <cstring>
#include <iostream>
#include <memory>
#include <boost/scoped_array.hpp>

namespace fur
{
   namespace
   {
      const char rarhead[7] = {0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00};
   }

   void check_rarhead(std::ifstream &file) throw(Exception)
   {
      char new_head[7];
      file.read(new_head, 7);
      bool flag = true;
      for (int i = 0; i < 7; ++i)
	 flag &= new_head[i] == rarhead[i];
      if (!flag)
      {
	 exception_throw(ef_rarhead(), ERarhead(__func__));
      }
   }

   void read_head(BaseHead *&head, std::ifstream &file) throw(Exception)
      try
      {
	 std::unique_ptr<BaseHead> tmphead;
	 head->read_base(file);
	 switch (head->headtype)
	 {
	    case HeadType::MAIN:
	       tmphead = std::unique_ptr<BaseHead>(new MainHead(*head));
	       break;
	    case HeadType::FILE:
	       tmphead = std::unique_ptr<BaseHead>(new FileHead(*head));
	       break;
	    case HeadType::END:
	       tmphead = std::unique_ptr<BaseHead>(new EndHead(*head));
	       break;
	    default:
	       dprint("%x\n", head->headtype);
	       throw EUnsupport(__func__);
	 }
	 tmphead->read_remain(file);
	 delete head;
	 head = tmphead.release();
	 if (head->headcrc != ((~head->tmpcrc.checksum()) & 0xffff))
	 {
	    exception_throw(ef_headcrc(), EHeadcrc(__func__));
	 }
      }
      catch(std::bad_alloc)
      {
	 throw EAllocation(__func__);
      }

   void BaseHead::read_base(std::ifstream &file) throw()
   {
      uint8_t tmp[5];
      uint8_t tmpht;
      file.read(reinterpret_cast<char*>(&headcrc), 2);
      file.read(reinterpret_cast<char*>(tmp), 1);
      memcpy(&tmpht, tmp, 1);
      headtype = static_cast<HeadType>(tmpht);
      file.read(reinterpret_cast<char*>(tmp) + 1, 2);
      memcpy(&flags, tmp + 1, 2);
      file.read(reinterpret_cast<char*>(tmp) + 3, 2);
      memcpy(&headsize, tmp + 3, 2);
      tmpcrc.process_bytes(tmp, 5);
   }

   void MainHead::read_remain(std::ifstream &file) throw(Exception)
      try
      {
	 boost::scoped_array<uint8_t> tmp(new uint8_t[headsize]);
	 file.read (reinterpret_cast<char*>(tmp.get()), headsize - 7);
	 memcpy(&reserved1, tmp.get(), 2);
	 memcpy(&reserved2, tmp.get() + 2, 4);
	 tmpcrc.process_bytes(tmp.get(), headsize - 7);
      }
      catch (std::bad_alloc)
      {
	 throw EAllocation(__func__);
      }
   
  
   void FileHead::read_remain(std::ifstream &file) throw(Exception)
      try
      {
	 boost::scoped_array<uint8_t> tmp(new uint8_t[headsize]);
	 file.read(reinterpret_cast<char*>(tmp.get()), headsize - 7);
	 memcpy(&packsize, tmp.get(), 4);
	 memcpy(&unpacksize, tmp.get() + 4, 4);
	 memcpy(&hostsystem, tmp.get() + 8, 1);
	 memcpy(&filecrc, tmp.get() + 9, 4);
	 memcpy(&filetime, tmp.get() + 13, 4);
	 memcpy(&version, tmp.get() + 17, 1);
	 memcpy(&method, tmp.get() + 18, 1);
	 memcpy(&namesize, tmp.get() + 19, 2);
	 memcpy(&fileattr, tmp.get() + 21, 4);
	 memcpy(filename, tmp.get() + 25, namesize);
	 filename[namesize] = 0;
	 tmpcrc.process_bytes(tmp.get(), headsize - 7);
      }
      catch (std::bad_alloc)
      {
	 throw EAllocation(__func__);
      }

   void EndHead::read_remain(std::ifstream &file) throw(Exception)
   {
   }

   BaseHead::BaseHead(const BaseHead& basehead) throw()
   {
      headcrc = basehead.headcrc;
      headtype = basehead.headtype;
      flags = basehead.flags;
      headsize = basehead.headsize;
      tmpcrc = basehead.tmpcrc;
   }
   
}//end of namespace fur
